﻿/* ================================================================================
<copyright file="PortLib.cs" company="The Framework Factory, LLC" lastWriteTime="12/05/2012 23:23:47 PM">
  Copyright (c) 2012 The Framework Factory, LLC.  All rights reserved.
</copyright>
=================================================================================== */
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace FWF.Logging.Net
{
    /// <summary>
    /// 
    /// </summary>
    public static class PortLib
    {

        private static readonly List<IPEndPoint> _inUseEndpoints = new List<IPEndPoint>();

        private const int StartPortLookup = 7700;

        private static readonly IFWFLog _log = LogFactory.Get( typeof ( PortLib ) );

        /// <summary>
        /// Initializes the <see cref="PortLib" /> class.
        /// </summary>
        static PortLib()
        {
            FetchInUseEndPoints();
        }

        /// <summary>
        /// Fetches the in use end points.
        /// </summary>
        private static void FetchInUseEndPoints()
        {
            try
            {
                IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();

                _inUseEndpoints.Clear();
                _inUseEndpoints.AddRange(
                    ipGlobalProperties.GetActiveTcpListeners()
                    );
            }
            catch( Exception ex )
            {
                _log.Error( "Unable to determine current active TCP listeners", ex );
            }
        }

        /// <summary>
        /// Gets the next available TCP port.
        /// </summary>
        /// <returns></returns>
        private static int GetNextAvailableTcpPort()
        {
            int portToTry = StartPortLookup;

            while ( true )
            {
                if ( !_inUseEndpoints.Exists( x => x.Port == portToTry ) )
                {
                    return portToTry;
                }

                portToTry++;
            }
        }

        /// <summary>
        /// Gets the available channel.
        /// </summary>
        /// <returns></returns>
        public static Uri RegisterNextAvailableChannel( string virtualPath )
        {
            // Loop through port values to determine an open port
            TcpChannel availableChannel;
            int portToTry = GetNextAvailableTcpPort();

            while ( true )
            {
                try
                {
                    availableChannel = new TcpChannel( portToTry );
                    break;
                }
                catch
                {
                    portToTry++;
                }
            }

            ChannelServices.RegisterChannel( 
                availableChannel, 
                false 
                );

            return new Uri(
                    string.Format(
                        "tcp://{0}:{1}/{2}",
                        Environment.MachineName,
                        portToTry,
                        virtualPath
                        )
                    );
        }


    }
}
